Skip to content

fix: lazily initialize AnnotationProvider in AsyncApiModule#238

Open
Ishita-190 wants to merge 2 commits intoasyncapi:masterfrom
Ishita-190:ishi
Open

fix: lazily initialize AnnotationProvider in AsyncApiModule#238
Ishita-190 wants to merge 2 commits intoasyncapi:masterfrom
Ishita-190:ishi

Conversation

@Ishita-190
Copy link

@Ishita-190 Ishita-190 commented Feb 17, 2026

Fixes: #237 (comment)

What

Made changes to : kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt
so that the AnnotationProvider performs classpath scanning lazily on the first request, instead of eagerly at application startup.

How

  • Wrapped the annotation scanning logic in asyncApiAnnotationExtension to defer execution until extend is called.
  • The classpath scanning now happens on the first request and not on application startup.

All changes work as intended.

Summary by CodeRabbit

  • Refactor
    • Streamlined AsyncAPI extension handling in the Ktor module so an annotation-based extension is always present in the pipeline; when no annotation data exists it behaves as a safe no-op, improving consistency and predictability.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our contributors guide useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Replaced a conditional, nullable annotation-based AsyncApiExtension with a concrete anonymous AsyncApiExtension that is always present; it defines a fixed order and lazily delegates to an annotation-derived extension when annotation data exists, otherwise delegating to a no-op implementation.

Changes

Cohort / File(s) Summary
AsyncAPI Extension Initialization
kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt
Replaced annotationProvider.asyncApi?.let { ... } pattern with an always-present anonymous AsyncApiExtension object. The object defines order and performs lazy delegation via AsyncApiExtension.from(...) when annotation data exists; otherwise it delegates to a no-op extension. This makes the extension part of the extensions list unconditionally while keeping annotation behavior conditional.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in code, with nibble and cheer,
An extension now present, yet gentle and clear.
It waits for annotations, then springs to play,
Or sits softly quiet when none come its way.
Hooray for tidy hops in the build today!

🚥 Pre-merge checks | ✅ 2 | ❌ 4

❌ Failed checks (3 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements lazy initialization but does not implement the core requirements from issue #237: preventing scans when baseClass is null, logging warnings, or validating baseClass before scanning. Implement the missing safety checks: skip AnnotationProvider creation when baseClass is null, add warning logs, and validate scanAnnotations and baseClass before proceeding with scanning [#237].
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (2 files):

⚔️ kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt (content)
⚔️ pom.xml (content)

These conflicts must be resolved before merging into master.
Resolve conflicts locally and push changes to this branch.
Title check ❓ Inconclusive The title mentions lazy initialization of AnnotationProvider, but this doesn't clearly describe the primary fix needed: preventing full-classpath scans when baseClass is null. Consider revising the title to more explicitly reflect the core issue: preventing unintended classpath scans when baseClass is null, such as 'fix: prevent full-classpath scan when baseClass is not configured'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Out of Scope Changes check ✅ Passed All changes in the PR are focused on the AsyncApiModule implementation and are directly related to the annotation scanning initialization issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch ishi
  • Post resolved changes as copyable diffs in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ishita-190 Ishita-190 changed the title fix: Lazily initialize AnnotationProvider in AsyncApiModule fix: lazily initialize AnnotationProvider in AsyncApiModule Feb 17, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt (1)

61-71: ⚠️ Potential issue | 🟡 Minor

The PR correctly defers classpath scanning to the first request.

The AnnotationProvider constructor does not trigger classpath scanning. The scanning happens lazily when the asyncApi property is first accessed (line 42 in AnnotationProvider.kt uses by lazy), and this property is only accessed when extend() is called on line 78. So the change achieves the stated objective of deferring classpath scanning from module initialization to first request.

However, the type declaration on line 73 is misleading: asyncApiAnnotationExtension is typed as AsyncApiExtension? but is always assigned a non-null object. Change to AsyncApiExtension. Additionally, AsyncApiExtension.from() is recreated on every extend() call; consider caching it after the first call for efficiency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt`
around lines 61 - 71, The asyncApiAnnotationExtension field is declared nullable
but always assigned and AsyncApiExtension.from() is recreated on every extend()
call; change asyncApiAnnotationExtension to non-null AsyncApiExtension and cache
the result of AsyncApiExtension.from(...) when first created so subsequent calls
to extend() reuse the same instance. Locate AnnotationProvider construction and
the extend() method, update the declaration of asyncApiAnnotationExtension to
type AsyncApiExtension (not AsyncApiExtension?), compute and store the
AsyncApiExtension instance once (e.g., in a private val cachedAsyncApiExtension
= AsyncApiExtension.from(...)) and have extend() reference that
cachedAsyncApiExtension instead of calling AsyncApiExtension.from() repeatedly.
Ensure nullability is removed and usages updated accordingly.
🧹 Nitpick comments (1)
kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt (1)

73-81: Nullable type is misleading — the extension is never null.

asyncApiAnnotationExtension is typed as AsyncApiExtension? but is always assigned a non-null anonymous object. Previously (with the ?.let pattern) it could be null when there were no annotations, causing listOfNotNull on line 106 to filter it out. Now it's always present in the extensions list and acts as a no-op when annotationProvider.asyncApi returns null.

This is functionally fine but the nullable type is misleading. Consider changing to AsyncApiExtension.

Additionally, the linked issue #237 specifies guards that aren't implemented here:

  • Skip creating the extension entirely when scanAnnotations == false.
  • Log a warning when scanAnnotations == true but baseClass == null.
  • Only scan when both conditions are met.

These requirements would prevent the unbounded classpath scan the issue describes.

Suggested type fix
-    private val asyncApiAnnotationExtension: AsyncApiExtension? =
+    private val asyncApiAnnotationExtension: AsyncApiExtension =
         object : AsyncApiExtension {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt`
around lines 73 - 81, The field asyncApiAnnotationExtension is declared nullable
but always initialized to a non-null anonymous AsyncApiExtension, which is
misleading and causes unintended scanning behavior; change its type to
AsyncApiExtension (remove the ?) and modify construction so the extension is
only created when scanAnnotations is true and baseClass is non-null: if
scanAnnotations is false do not create/append the extension, if scanAnnotations
is true but baseClass is null log a warning and do not create it, and when
creating the extension make it consult annotationProvider.asyncApi as before (so
listOfNotNull usage at listOfNotNull(...) will correctly include or exclude the
extension); update references to asyncApiAnnotationExtension accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt`:
- Around line 61-71: The asyncApiAnnotationExtension field is declared nullable
but always assigned and AsyncApiExtension.from() is recreated on every extend()
call; change asyncApiAnnotationExtension to non-null AsyncApiExtension and cache
the result of AsyncApiExtension.from(...) when first created so subsequent calls
to extend() reuse the same instance. Locate AnnotationProvider construction and
the extend() method, update the declaration of asyncApiAnnotationExtension to
type AsyncApiExtension (not AsyncApiExtension?), compute and store the
AsyncApiExtension instance once (e.g., in a private val cachedAsyncApiExtension
= AsyncApiExtension.from(...)) and have extend() reference that
cachedAsyncApiExtension instead of calling AsyncApiExtension.from() repeatedly.
Ensure nullability is removed and usages updated accordingly.

---

Nitpick comments:
In
`@kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt`:
- Around line 73-81: The field asyncApiAnnotationExtension is declared nullable
but always initialized to a non-null anonymous AsyncApiExtension, which is
misleading and causes unintended scanning behavior; change its type to
AsyncApiExtension (remove the ?) and modify construction so the extension is
only created when scanAnnotations is true and baseClass is non-null: if
scanAnnotations is false do not create/append the extension, if scanAnnotations
is true but baseClass is null log a warning and do not create it, and when
creating the extension make it consult annotationProvider.asyncApi as before (so
listOfNotNull usage at listOfNotNull(...) will correctly include or exclude the
extension); update references to asyncApiAnnotationExtension accordingly.

@Ishita-190
Copy link
Author

@lorenzsimon I've made the changes, please take a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] : Annotation scanning runs with baseClass = null, causing full classpath scan

1 participant